Skip to content

feat(mcp-server): Ship @sentry/mcp-server as a slim bundle that shares CLI auth - #1731

Open
sentry-junior[bot] wants to merge 40 commits into
mainfrom
ref/retire-mcp-server-package
Open

sentry-junior[bot] wants to merge 40 commits into
mainfrom
ref/retire-mcp-server-package

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Stack: this branch merges #1766 (route registry), #1765 (lazy syntax highlighting), and #1763 (Zod English locale) with merge commits. Review those first; this PR's own changes are 261e371f (MCP SDK v2), ffeaf9ba (unified entry), 7a2f0a2a (stdio-auth docs), 69e65252 (lone -v), 9977bb0c (no mcp-core import in the command tree), c0243b5a (MCP SDK v1 removed from mcp-core), 07b8d4c8 (zod/v3 stub in both bundles), ccdd87ab (--access-token redacted from telemetry), and a98cb249 (--access-token as a deprecated alias). #1764 is not merged: it only touched packages/mcp-server/src/index.ts, which this PR deletes, and its lazy-loading idea is applied to the unified entry here.

@sentry/mcp-server stays the recommended stdio install. Its server code moves into the CLI so it shares the CLI's auth, and the package becomes a slim, dependency-free bundle of that code (see the @sentry/mcp-server section below). The CLI exposes the same server as sentry mcp. This also locks in a one-way package graph, so MCP tools can later be built on the CLI's portable operations without a cycle.

What moved where

  • stdio transport: packages/mcp-server/src/transports/stdio.ts → packages/mcp-core/src/transports/stdio.ts, exported as @sentry/mcp-core/transports/stdio. It now uses @sentry/core tracing, so mcp-core takes no Node SDK dependency.
  • Server host: mcp is a real Stricli command (packages/cli/src/commands/mcp.ts) with its flags declared, so help is generated like every other command. Env merging and config validation live in packages/cli/src/lib/mcp/{args,config}.ts, agent provider and telemetry setup in lib/mcp/server.ts. mcp-core stays free of CLI concepts. The command loads lib/mcp.ts, and that loads the server stack, only when it serves. The command module itself imports nothing from mcp-core (its help copies the skill names, and a test checks them against ALL_SKILLS), so docs generation and pnpm run cli still work without building mcp-core.
  • Standalone auth (device code flow, bundled client ID, ~/.sentry/mcp.json cache, auth subcommands): deleted. CLI auth replaces it.
  • Consumers: evals import @sentry/mcp-core/transports/stdio. The test client launches sentry mcp from CLI source. Smoke tests only target the hosted worker, so they needed no change. ensure-generated.mjs now sends generator output to stderr, so a first pnpm run cli mcp cannot write junk into the JSON-RPC stream.
  • Refresh after a 401: mcp-core copies the handler context per call, so a token returned from onUpstreamUnauthorized was dropped before. The refreshed token now reaches the context, and a regression test checks that later calls use it.
  • Install snippets and docs lead with npx @sentry/mcp-server@latest; the web install instructions are unchanged. User-facing docs mention the shared CLI login but do not steer installs to sentry mcp; the mcp-qa skill drives the server from CLI source. docs/operations/stdio-auth.md and docs/releases/stdio.md are rewritten package-first, and stdio-auth.md documents that each server process uses one account.

Parity

Old @sentry/mcp-server New @sentry/mcp-server (and sentry mcp)
--access-token / SENTRY_ACCESS_TOKEN Deprecated alias. Runs as if SENTRY_AUTH_TOKEN=<token> and SENTRY_FORCE_ENV_TOKEN=1 were set, through the same env-token path, so it wins over a stored login. It also replaces an existing SENTRY_AUTH_TOKEN/SENTRY_TOKEN, since the flag is the explicit choice. The flag wins over the env var. The token is sent to the --host/--url target as before; an org token (sntrys_) issued for another host is refused with HostScopeError. Prints one deprecation warning on stderr, without the token; the flag value is redacted from telemetry. Docs, help, and server.json now lead with SENTRY_AUTH_TOKEN (+ SENTRY_FORCE_ENV_TOKEN=1, and SENTRY_HOST/SENTRY_URL for self-hosted).
No token: device code flow plus ~/.sentry/mcp.json cache CLI session (sentry auth login, or SENTRY_AUTH_TOKEN/SENTRY_TOKEN), refreshed after an upstream 401. In a TTY it starts the CLI login. Over pipes it fails with an error naming the login command: npx -y @sentry/mcp-server auth login from the package, sentry auth login from sentry mcp. The old cache is not read.
--host / SENTRY_HOST, --url / SENTRY_URL Same flags. The env values come from the CLI's configured URL, which is translated into --host/--url. One difference: an http:// SENTRY_URL now maps to --host plus --insecure-http (the CLI's self-hosted behavior) instead of being rejected.
--insecure-http Same. It is still rejected with --url and for sentry.io hosts.
--mcp-url / MCP_URL, --sentry-dsn / SENTRY_DSN / DEFAULT_SENTRY_DSN Same
--skills / MCP_SKILLS, --all-skills, --disable-skills / MCP_DISABLE_SKILLS Same, including dropping seer from the defaults on self-hosted hosts
--agent-provider / EMBEDDED_AGENT_PROVIDER, --openai-base-url, --openai-model / OPENAI_MODEL, --anthropic-base-url, --anthropic-model / ANTHROPIC_MODEL, provider API keys Same, with the same provider mismatch and auto-detection warnings
--organization-slug, --project-slug, --experimental Same
--help / -h, --version / -v Stricli's generated help. sentry-mcp --help lists mcp, auth, and help; sentry-mcp mcp --help lists the flags and env vars. A lone -v still prints the version; anywhere else -v is the CLI's --verbose. Changed: the output is the bare version (0.48.0), like the CLI, instead of sentry-mcp 0.48.0.
--flag= (for example --sentry-dsn= to turn off telemetry), values with a line break Same. Stricli does not read these joined forms, so the runner splits them into --flag value first (splitInlineFlagValues).
Unknown flags or positionals Rejected by Stricli (exit 252)
SENTRY_CLIENT_ID, SENTRY_MCP_AUTH_CACHE Ignored, because the device flow is gone
sentry-mcp auth login|logout|status The CLI's real auth commands: login (with all its flags, including --url), logout, status, list, switch, refresh, token, whoami. They use the CLI session. Account pickers take the account as an argument, since the Ink picker is not bundled.

Dependency direction guard

sentry → @sentry/mcp-core → @sentry/toolkit-core, and @sentry/mcp-cloudflare → @sentry/mcp-core. No edge runs the other way. pnpm run lint enforces this with two checks:

  • An oxlint no-restricted-imports override bans sentry, sentry/**, and relative CLI paths inside packages/mcp-core and packages/toolkit-core.
  • scripts/check-workspace-deps.mjs (lint:deps) reads every workspace package.json. It fails on any cycle, on mcp-core reaching sentry or mcp-cloudflare, and on toolkit-core reaching sentry or mcp-core.

The rule is documented in AGENTS.md and docs/architecture/overview.md#package-dependency-direction. Capabilities that only the CLI host has (SQLite cache, DSN detection from local files, file uploads) are injected into mcp-core through interfaces defined in mcp-core or toolkit-core. sentry mcp already works this way for the access token and refresh callback in ServerContext.

Release and MCP registry

Every CLI release now publishes @sentry/mcp-server at the CLI's version, so the next one goes from 0.42.0 to the CLI's next version (0.48.0). The private mcp-* packages keep their own versions.

  • packages/cli/script/bump-version.ts sets packages/mcp-server/package.json to the CLI version on --pre and to the next -dev version on --post. The bundle script fails if the two versions differ.
  • cli-build.yml bundles, tests, and packs the package and uploads it as mcp-server-npm-package. The packages/cli Craft workspace publishes it through a second npm target and attaches it to the cli@<version> GitHub release.
  • The separate packages/mcp-server Craft workspace, the Release workflow's mcp option, and the packing steps in merge-jobs.yml are removed.
  • mcp-registry.yml now runs on stable cli@ releases and strips the prefix. A manual run with -f version=X uses cli@X, or the bare tags used up to 0.42.0. mcpName: io.github.getsentry/sentry-mcp is unchanged; server.json's environment variables now declare SENTRY_AUTH_TOKEN (required, secret) and SENTRY_FORCE_ENV_TOKEN (default 1) instead of SENTRY_ACCESS_TOKEN.

@sentry/mcp-server bundle

@sentry/mcp-server stays the recommended, supported package. It ships one dependency-free file built from the CLI's own runner, so the package and sentry mcp run the same code, including self-hosted Sentry.

  • Entry: packages/cli/src/mcp-server.ts runs cli.ts's startCli() with mcp-server-app.ts, a route map of the CLI's mcp (the default command), auth, and help. The runner takes a CliEntry that loads the application, so sentry (sentry-cli.ts) and sentry-mcp differ only in their route map. lib/application.ts holds the Stricli application setup that used to sit in app.ts. packages/mcp-server/script/bundle.ts bundles the entry into packages/mcp-server/dist/sentry-mcp.cjs.
  • Running mcp: isMcpInvocation() finds mcp after leading global flags, or no command at all where mcp is the default. The runner then runs Stricli without the CLI's telemetry, recovery middleware, and update check (the server owns stdout and has its own telemetry). The command resolves when the client closes stdin, and only then does the runner close the dispatcher and arm the force-exit backstop.
  • Names: lib/app-identity.ts replaces login-command.ts and usage.ts. Each entry registers its name, and hints (AuthError, refresh and device-flow failures, host-scope errors) and help examples use it: sentry auth login, sentry-mcp auth login, or npx -y @sentry/mcp-server auth login when npx launched the process (npm_command=exec or an _npx path). MCP telemetry keeps app.server.distribution at sentry mcp or @sentry/mcp-server.
  • Lazy loading (from perf(mcp): Load the server stack only when the stdio server starts #1764): --help, --version, and auth … do not load mcp-core's server, the provider factory, the MCP telemetry, or the stdio transport; commands/mcp.ts imports lib/mcp.ts only when it serves, and lib/mcp.ts imports lib/mcp/server.ts only after setup checks pass. A bare --version (or lone -v) returns before the application loads. --help and auth still load @sentry/node, because the CLI's command builder and error reporting import it, same as sentry --help.
  • MCP SDK v2 (261e371f, separate commit): stdio uses @modelcontextprotocol/server and its stdio transport, like the Cloudflare worker. startStdio() returns a closed promise; v2's transport closes when stdin ends, which is what lets the command return. c0243b5a then removes SDK v1 from mcp-core: buildServer() always builds a v2 McpServer, the sdkVersion option is gone, and @modelcontextprotocol/sdk is only a dev dependency of mcp-core (its tests use the in-memory client). The Cloudflare worker keeps its own SDK dependency for StreamableHTTPClientTransport. Both sentry mcp and the bundle serve 14 tools over v2 and exit 0 within ~13–16 ms of stdin closing.
  • Size work: perf(cli): Bundle only Zod's English locale #1763's Zod English-locale plugin; __SENTRY_CLI_SYNTAX_HIGHLIGHT__=false (perf(cli): Load syntax highlighting lazily and allow compiling it out #1765) and a new __SENTRY_CLI_INK_UI__=false (lib/select-prompt.ts) compile out highlight.js/parse5 and Ink; zod/v3 resolves to a shared stub (packages/cli/script/lib/zod-v3-stub-plugin.ts) in both this bundle and the CLI's (script/bundle.ts, script/build.ts): @ai-sdk/provider-utils and @mastra/schema-compat only reach it for Zod 3 inputs, and nothing we bundle builds Zod 3 schemas. The stub keeps ZodType a class so Mastra's instanceof check is false, and throws a clear error if a Zod 3 path ever runs. A test bundles the real Mastra client and provider-utils, runs a workflow create/start, and converts Zod 4 and JSON schemas. With SDK v1 gone from mcp-core, the bundle no longer needs a v1 stub and fails if any @modelcontextprotocol/sdk module reaches it.
  • Left out: the other CLI commands, agent skills, shell completions, the sixel dashboard, syntax highlighting, the Ink UI, sentry init, and build/sourcemap/debug-file code. The SQLite WASM fallback is also left out, so the engine moves from >=22.13 to >=22.15 (native node:sqlite), and the bin exits early with a clear error on older Node.js.
  • Guards: the bundle script fails if any of those modules (or any @modelcontextprotocol/sdk module) contributes code to the output, or if the file grows past 3.75 MiB (MAX_BUNDLE_BYTES, ~240 KB over the measured size; left as is, since removing SDK v1 saved only ~1 KB here, where it was already stubbed).
  • Tests and CI: packages/mcp-server/test/bundle.test.mjs runs the built file: initialize plus tools/list over stdio with --sentry-dsn= (with SENTRY_AUTH_TOKEN, and with the deprecated --access-token, checking the warning is on stderr once and never echoes the token), exit on stdin close, no runtime dependencies, bare --version and -v, --help listing exactly mcp/auth/help, mcp --help examples naming sentry-mcp, the login hint with and without npx, auth status, and rejecting other CLI commands. cli-build.yml bundles, tests, and packs the package next to the CLI's npm package.

The old @sentry/mcp-server/transports/stdio export is gone (now @sentry/mcp-core/transports/stdio), and the standalone clean-consumer workflow (mcp-server-package.yml) is removed; the bundle test replaces it.

Release note: do not run npm deprecate. Users on Node.js 22.13 or 22.14 need to upgrade Node.js.

Measurements (Node 22, this sandbox; startup rows are the median of 7 runs, idle RSS is 5 runs)

Before restack (53fc92fe) 9977bb0c
Bundle 3,663,084 B 3,690,124 B (3,689,429 B at a98cb249)
sentry-mcp --version 137 MB / ~335 ms (sibling scratch) 86 MB / 133–138 ms
sentry-mcp --help – 153 MB / 352 ms
sentry-mcp auth status (logged out) – 158 MB / 444 ms
Idle server after initialize + tools/list – 152–153 MB RSS

--help, auth status, and idle RSS were measured at 7a2f0a2a; the two later commits only add the -v fast path and move a string list, and the bundle grew 89 bytes.

CLI dist/index.cjs: 7,426,174 B before c0243b5a → 7,203,971 B without SDK v1 (−222 KB; ajv and most of zod-to-json-schema go with it) → 7,142,739 B with the zod/v3 stub (−61 KB) → 7,143,333 B at a98cb249.

The bundle ends up about the same size as before even with 550 KB of savings (Zod locales −264 KB, SDK v1 and Zod 3 −280 KB), because it now carries the CLI runner, the full auth commands, and generated help. Largest inputs: @sentry/api 482 KB, @modelcontextprotocol/server 291 KB (156 KB of it ajv), @sentry/server-utils 242 KB, zod 193 KB, mcp-core's OTel semantics 187 KB, @sentry/core 162 KB.

mcp-core CJS output

Nothing builds or consumes mcp-core CJS anymore. #1732, now merged into this branch from main, switched packages/mcp-core/tsdown.config.ts to format: ["esm"]. mcp-core is "type": "module", and every exports entry maps only types and default (.js) with no require condition. The CLI's esbuild bundle (CJS output) resolves default and converts the ESM itself, and scripts/check-build-imports.ts now checks dist/server.js. This PR does not touch the build format.

Verification

  • pnpm run tsc, pnpm run lint (includes lint:deps), and CLI ci:policy pass.
  • pnpm run test: toolkit-core, mcp-core, mcp-cloudflare, mcp-test-client, docs-mcp, local, and cli-docs pass. CLI: 12,807 pass; the 6 failures are chmod-based tests (completions EACCES/EPERM, read-only DB repair, upgrade lock permissions, unreadable DSN file, file-changes I/O) that fail in this sandbox's filesystem and do not touch this diff. The MCP, auth, app-identity, startup, help, and completion suites pass.
  • pnpm --filter sentry run generate:docs passes with packages/mcp-core/dist removed, which is the sentry-cli-docs CI condition.
  • New or rewritten tests: test/lib/mcp/args.test.ts parses with the command's real Stricli parameters (test/lib/mcp/parse-flags.ts); mcp-start.test.ts and mcp-auth.test.ts drive serveMcp() and withConfiguredTarget() through it; mcp-command.test.ts covers isMcpInvocation, the {mcp, auth, help} app, no CLI telemetry for mcp, teardown only after the server returns, setup failures, --help, the help's skill list against ALL_SKILLS, and unknown flags; app-identity.test.ts covers names and npx hints. usage*.test.ts, mcp/auth.test.ts, and mcp-run.test.ts are deleted with their modules.
  • pnpm --filter @sentry/mcp-server run bundle (with SENTRY_AUTH_TOKEN unset) builds 3,689,429 B at a98cb249; test:bundle passes 13/13.
  • The CLI bundle builds; node dist/bin.cjs mcp answers tools/list with 14 tools over SDK v2 and exits 0 when stdin closes. With --access-token, stdout carries only JSON-RPC frames and stderr has the one deprecation line, without the token. CLI test/e2e/bundle.test.ts + library.test.ts pass 26/26.
  • At a98cb249: pnpm run lint passes; tsc passes for cli, mcp-core, mcp-cloudflare, mcp-server-evals, and mcp-test-client; mcp-core (1,890), mcp-cloudflare (443), and mcp-test-client (82) tests pass. CLI test/lib test/commands test/types test/script: 12,816 pass, 11 fail, and all 11 fail the same way without this change (6 chmod-based tests, plus 5 login-hint wording tests in errors, oauth, and auto-login-host-guard that see npx -y sentry because the sandbox runs vitest through npx).
  • --access-token tests (mcp-start.test.ts): it serves the same token and refresh result as SENTRY_AUTH_TOKEN + SENTRY_FORCE_ENV_TOKEN=1, overrides a stored login before and after startup, overrides SENTRY_AUTH_TOKEN/SENTRY_TOKEN, leaves process.env untouched, prints the hint once to stderr and nothing to stdout, and keeps the token out of the hint and of MalformedAuthTokenError/HostScopeError. The existing self-hosted, org-token, and .sentryclirc cases still pass.
  • craft config --workspace packages/cli and the release checks above are unchanged from the earlier verification.

Noticed, not changed: serverInfo.version in initialize reports mcp-core's package version (0.42.0), as it did before this PR, not the CLI/package version.

via @BYK.

--

View Session in Sentry

Move the stdio MCP host into the CLI so `sentry mcp` is the only local
server. The stdio transport moves to mcp-core; flag/env parsing, usage, and
server startup move to packages/cli/src/lib/mcp/. The standalone device-code
auth and token cache are deleted in favor of CLI auth.

`sentry mcp` keeps every standalone flag and env var, and accepts
`--access-token` / `SENTRY_ACCESS_TOKEN` as a static, never-refreshed token
that still goes through host scoping.

Lock the dependency direction (cli -> mcp-core -> toolkit-core) with an
oxlint import ban and a workspace manifest cycle check, move the MCP
registry entry to the `sentry` package, and reduce @sentry/mcp-server to a
deprecation shim for one last release.

Co-Authored-By: BYK <burak.kaya@sentry.io>
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Ready Ready Preview Oct 31, 2026 9:15am UTC

Request Review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/lib/mcp/args.ts Resolved
The doc example checker reads backticked sentry commands in the generated
configuration page and does not know about `sentry mcp` passthrough flags.

Co-Authored-By: BYK <burak.kaya@sentry.io>
The retired package fixed the SDK environment to "stdio" at build time.

Co-Authored-By: BYK <burak.kaya@sentry.io>
Comment thread .oxlintrc.json Resolved
Comment thread packages/cli/src/lib/mcp.ts Resolved
sentry-junior Bot and others added 3 commits October 29, 2026 20:17
…nippet

Tool handlers read mcp-core's own copy of the server context, so updating
the host's context after a refresh had no effect. onUpstreamUnauthorized
can now return the new token, and mcp-core stores it where handlers read
it. The fx profile snippet also ran plain `npx sentry@latest`.

Co-Authored-By: BYK <burak.kaya@sentry.io>
…-package

Co-Authored-By: BYK <burak.kaya@sentry.io>
Oxlint replaces a rule's options per override, so the toolkit-core browser
override silently dropped the earlier CLI ban. Carry the ban there and test
the last matching override for each package.

Co-Authored-By: BYK <burak.kaya@sentry.io>
Comment thread packages/cli/src/lib/mcp/config.ts Resolved
@sentry-junior sentry-junior Bot changed the title ref(mcp): Retire @sentry/mcp-server in favor of sentry mcp ref(mcp): Move the stdio MCP server into sentry mcp Oct 29, 2026
sentry-junior Bot and others added 2 commits October 29, 2026 22:49
…-package

# Conflicts:
#	pnpm-lock.yaml

Co-Authored-By: BYK <burak.kaya@sentry.io>
Bundle the CLI's stdio MCP server into one dependency-free file instead of
forwarding to the sentry CLI. The package keeps its flags, env vars, and
self-hosted support, and adds a plain-text `sentry-mcp auth login`.

The bundle script keeps CLI-only code out (commands, markdown renderer,
agent skills, completions, terminal images, Ink UI, debug files) and drops
the SQLite WASM fallback, so the package requires Node.js 22.15+. It fails
the build if a CLI-only module reaches the output or the file exceeds 4.5 MB.

Co-Authored-By: BYK <burak.kaya@sentry.io>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/lib/mcp/run.ts Resolved
These invocations finish like any other drained command but skipped the
force-exit backstop and dispatcher close that runCli applies, so leftover
handles could keep the process alive on macOS. startMcpServer now reports
whether a server is running, and runMcpServer and the standalone auth entry
release resources whenever the invocation is finite.
@sentry-junior sentry-junior Bot changed the title ref(mcp): Move the stdio MCP server into sentry mcp feat(mcp): Run stdio MCP from sentry mcp and ship @sentry/mcp-server as its slim bundle Oct 29, 2026
sentry-junior Bot and others added 6 commits October 30, 2026 23:41
…-package

Co-Authored-By: BYK <burak.kaya@sentry.io>
…/retire-mcp-server-package

Co-Authored-By: BYK <burak.kaya@sentry.io>
…into ref/retire-mcp-server-package

Co-Authored-By: BYK <burak.kaya@sentry.io>
…ef/retire-mcp-server-package

Co-Authored-By: BYK <burak.kaya@sentry.io>
`sentry mcp` and `@sentry/mcp-server` now build the server with
`sdkVersion: "v2"` and connect it through `@modelcontextprotocol/server`'s
stdio transport, the SDK the Cloudflare worker already runs. Both serve
the same 14 tools as before.

v2's transport closes when the client closes stdin, and `startStdio()`
now returns a `closed` promise for that, so a host can tear down only
after the session ends. `buildServer()` still defaults to v1 for other
callers.

Co-Authored-By: BYK <burak.kaya@sentry.io>
`@sentry/mcp-server` no longer has its own entry logic. `mcp-server.ts`
runs `cli.ts`'s runner with `mcp-server-app.ts`, a route map of the
CLI's real `mcp` (the default command), `auth`, and `help`. So
`sentry-mcp auth status|list|switch|logout` work, and flags and help
for `sentry mcp` and `sentry-mcp` come from one Stricli definition.

- The runner takes a `CliEntry` that loads the application, and starts
  `mcp` without the CLI's telemetry, recovery middleware, and update
  check. The command resolves when the client closes stdin, and only
  then does the runner release the network.
- `lib/app-identity.ts` replaces `login-command.ts`: hints and help
  examples name `sentry-mcp`, or `npx -y @sentry/mcp-server` under npx.
- `--flag=` and values with line breaks still parse, as they did with
  the old parser (`splitInlineFlagValues`).
- A bare `--version` answers before the application loads (86 MB,
  140 ms). The server stack loads only when `mcp` serves.
- The bundle adds #1763's Zod English-locale plugin, compiles out syntax
  highlighting and the Ink picker, and stubs MCP SDK v1's `McpServer`
  and `zod/v3`. It is 3,689,911 bytes; the budget is 3.75 MiB.

Co-Authored-By: BYK <burak.kaya@sentry.io>
Co-Authored-By: BYK <burak.kaya@sentry.io>
@github-actions github-actions Bot removed the risk: high PR risk score: high label Oct 31, 2026
@github-actions github-actions Bot added the risk: high PR risk score: high label Oct 31, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7a2f0a2. Configure here.

Comment thread packages/cli/src/mcp-server.ts Resolved
@github-actions github-actions Bot removed the risk: high PR risk score: high label Oct 31, 2026
The old package treated `-v` as `--version`. In the unified build the CLI's
global flags own `-v` as `--verbose`, so `sentry-mcp -v` started the
server instead. Answer a lone `-v` from the same fast path as `--version`.

Co-Authored-By: BYK <burak.kaya@sentry.io>
@github-actions github-actions Bot added the risk: high PR risk score: high label Oct 31, 2026
@github-actions github-actions Bot removed the risk: high PR risk score: high label Oct 31, 2026
`commands/mcp.ts` imported `@sentry/mcp-core/skills` for its help text, so
loading any command needed mcp-core's build output. Docs generation
(`sentry-cli-docs`, cli-build's docs job) and a fresh `pnpm run cli`
load every command without building mcp-core, and failed. Copy the skill
names into the command and guard the copy with a test.

Co-Authored-By: BYK <burak.kaya@sentry.io>
Comment on lines +285 to +289
try {
console.error(label, error);
} catch {
// stderr may already be closed
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fatal MCP errors exit 0 when stderr EPIPE fires during console.error

exitAfterError always intends process.exit(1), but its console.error hits the entrypoint stderr EPIPE handler (installStreamErrorHandlers), which calls process.exit(0) first—so crashes after the client closes pipes report success.

Evidence
  • bin.ts and mcp-server.ts both call installStreamErrorHandlers(), which does process.exit(0) on stderr EPIPE (see lib/stream-errors.ts).
  • exitAfterError writes with console.error(label, error) even when it expects pipes to be closed (comment references stdio server spins forever at 100%+ CPU when the client dies: uncaughtException handler re-throws EPIPE on a broken stderr #1274), and only then schedules process.exit(1).
  • With an error listener on stderr, a write EPIPE is emitted to that handler rather than thrown into the try/catch, so process.exit(0) runs before the fatal process.exit(1) paths.
  • installProcessHandlers tests mock console.error and never install the stream handlers, so they do not catch this exit-code inversion.
Also found at 1 additional location
  • packages/cli/src/lib/stream-errors.ts:15-16

Identified by Warden · code-review · BU4-DK5

!(hasSentryTarget(flags) || (staticToken && parseSntrysClaim(staticToken)))
) {
const { assertRcUrlTrusted } = await import("./sentryclirc.js");
await assertRcUrlTrusted(process.cwd());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP startup fails when its working directory has been deleted

When sentry mcp starts from a deleted worktree, this call evaluates process.cwd() and throws ENOENT before the rc trust check can no-op, so the server does not start. Use the resolved command-context cwd or the same PWD fallback as buildContext.

Evidence
  • buildContext uses resolveCwd, which falls back to absolute PWD when process.cwd() throws; runMcpServer builds that context before dispatching the MCP command.
  • mcpCommand calls serveMcp(flags) without passing the context cwd, and serveMcp calls process.cwd() again when no explicit target or claimed org token bypasses the trust check.
  • The process.cwd() argument is evaluated before assertRcUrlTrusted runs, so even the no-rc-URL no-op cannot prevent the error; runMcpServer catches it and exits without starting the server.

Identified by Warden · code-review · 5YZ-RXX

sentry-junior Bot and others added 4 commits October 31, 2026 08:40
Every caller (sentry mcp, the Cloudflare worker, the eval mock server)
already asked for SDK v2, so buildServer() now always builds a v2
McpServer and the sdkVersion option, its overloads, and the v1 branch are
gone. mcp-core's remaining SDK v1 imports were types; they now come from
@modelcontextprotocol/server, and the unused RegisteredToolHandlerExtra
type is deleted. @modelcontextprotocol/sdk moves to mcp-core's
devDependencies (tests drive the server with its in-memory client) and
leaves mcp-server-evals, which never imported it.

The @sentry/mcp-server bundle no longer needs its SDK v1 stub, and now
fails if any @modelcontextprotocol/sdk module reaches it. The CLI bundle
drops SDK v1's server, zod-compat, its ajv, and zod-to-json-schema:
dist/index.cjs goes from 7,426,174 to 7,203,971 bytes.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
Everything we bundle builds Zod 4 schemas, but two dependencies still
import zod/v3 for Zod 3 inputs: @ai-sdk/provider-utils (zod3Schema and its
copy of zod-to-json-schema) and @mastra/schema-compat (its Zod 3 converter,
plus an instanceof z3.ZodType check in isZodV3). sentry init only uses
Mastra's workflow client, which converts no schemas.

zodV3StubPlugin replaces zod/v3 with a stub shared by script/bundle.ts,
script/build.ts, and the @sentry/mcp-server bundle. ZodType stays a class so
the instanceof check is false, and ZodFirstPartyTypeKind throws a clear
error if a Zod 3 schema ever reaches a converter. The test bundles the real
Mastra client and provider-utils, runs a workflow create/start, converts
Zod 4 and JSON schemas, and checks no zod/v3 module is included.

CLI dist/index.cjs: 7,203,971 -> 7,142,739 bytes.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
`sentry mcp` and `@sentry/mcp-server` take a token in --access-token, but
setFlagContext only redacted `token`, so the value became a
`flag.access-token` tag. Add it to the flag and argv redaction lists.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
--access-token and SENTRY_ACCESS_TOKEN used to bypass the CLI's credential
resolution with a static-token path of their own. They now run the server
as if SENTRY_AUTH_TOKEN=<token> and SENTRY_FORCE_ENV_TOKEN=1 were set: the
token goes into an env copy (withEnv), so it takes the CLI's env-token path,
wins over a stored login, and replaces any SENTRY_AUTH_TOKEN/SENTRY_TOKEN
already set, since the flag is the explicit choice. The flag wins over
SENTRY_ACCESS_TOKEN. The copy's URL is pinned to the --host/--url target, as
the old static token was; an org token's claim must still match it. The
.sentryclirc check runs against the invocation's own credentials first.

Either one prints a single deprecation warning on stderr (stdout is the
MCP transport) that never includes the token. resolveStaticMcpAccessToken
and the accessToken config fields are gone.

The help text, stdio-auth, READMEs, server.json, and the testing docs now
lead with SENTRY_AUTH_TOKEN (plus SENTRY_FORCE_ENV_TOKEN=1, and
SENTRY_HOST/SENTRY_URL for self-hosted). The test client and the eval
scorer launch the server with those env vars instead of --access-token.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
Comment on lines +143 to +146
const server = new McpServer({
name: MCP_SERVER_NAME,
version: LIB_VERSION,
};

if (sdkVersion === "v2") {
const server = new ModernMcpServer(serverInfo);
const registrations = configureServer({
server,
context: contextWithModes,
experimentalMode,
tools: customTools,
});
for (const { name, config, handler } of registrations) {
server.registerTool(name, config, handler);
}
if (registrations.length > 0) {
server.server.registerCapabilities({ tools: { listChanged: false } });
}
return wrapMcpServerWithSentry(server);
}

const server = new LegacyMcpServer(serverInfo);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity vulnerability may affect your project—review required:
Line 143 lists a dependency (@modelcontextprotocol/server) with a known High severity vulnerability.

ℹ️ Why this matters

Affected versions of @modelcontextprotocol/sdk and @modelcontextprotocol/server are vulnerable to Allocation of Resources Without Limits or Throttling. McpServer places no limit on how many array elements and object keys it validates in tool-call arguments. A single unauthenticated HTTP request within the default 4 MiB body limit can exhaust the Node.js heap during validation and crash the server process (denial of service). After upgrading, also set the opt-in maxToolInputElements McpServer option and lower maxRequestBodySize.

References: GHSA

To resolve this comment:
Check if you expose the McpServer over an HTTP transport (Streamable HTTP, SSE, or createMcpHandler) rather than stdio only and it registers tools that accept input, especially arrays or records.

  • If you're affected, upgrade this dependency to at least version 2.3.0 at pnpm-lock.yaml.
  • If you're not affected, comment /fp we don't use this [condition]
💬 Ignore this finding

To ignore this, reply with:

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

You can view more details on this finding in the Semgrep AppSec Platform here.

This branch was successfully deployed

2 active deployments
Preview — a98cb249 Deployed Oct 31, 2026 by vercel[bot]
Actions — a98cb249 Deployed Oct 31, 2026 by sentry-junior[bot] via eval #1438
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants